VERSION 1.0 CLASS
BEGIN
  MultiUse = -1  'True
  Persistable = 0  'NotPersistable
  DataBindingBehavior = 0  'vbNone
  DataSourceBehavior  = 0  'vbNone
  MTSTransactionMode  = 0  'NotAnMTSObject
END
Attribute VB_Name = "clsFakeTex"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = True
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
Option Explicit

Private Enum enumVBChar
 ["\0"] = 0
 ["\t"] = 9
 ["\n"] = 10
 ["\r"] = 13
 [" "] = 32
 ["!"] = 33
 ["""] = 34
 ["#"] = 35
 ["$"] = 36
 ["%"] = 37
 ["&"] = 38
 ["'"] = 39
 ["("] = 40
 [")"] = 41
 ["*"] = 42
 ["+"] = 43
 [","] = 44
 ["-"] = 45
 ["."] = 46
 ["/"] = 47
 ["0"] = 48
 ["1"] = 49
 ["2"] = 50
 ["3"] = 51
 ["4"] = 52
 ["5"] = 53
 ["6"] = 54
 ["7"] = 55
 ["8"] = 56
 ["9"] = 57
 [":"] = 58
 [";"] = 59
 ["<"] = 60
 ["="] = 61
 [">"] = 62
 ["?"] = 63
 ["@"] = 64
 ["a"] = 65
 ["b"] = 66
 ["c"] = 67
 ["d"] = 68
 ["e"] = 69
 ["f"] = 70
 ["g"] = 71
 ["h"] = 72
 ["i"] = 73
 ["j"] = 74
 ["k"] = 75
 ["l"] = 76
 ["m"] = 77
 ["n"] = 78
 ["o"] = 79
 ["p"] = 80
 ["q"] = 81
 ["r"] = 82
 ["s"] = 83
 ["t"] = 84
 ["u"] = 85
 ["v"] = 86
 ["w"] = 87
 ["x"] = 88
 ["y"] = 89
 ["z"] = 90
 ["lll"] = 91
 ["\"] = 92
 ["rrr"] = 93
 ["^"] = 94
 ["_"] = 95
 ["`"] = 96
 ["aa"] = 97
 ["bb"] = 98
 ["cc"] = 99
 ["dd"] = 100
 ["ee"] = 101
 ["ff"] = 102
 ["gg"] = 103
 ["hh"] = 104
 ["ii"] = 105
 ["jj"] = 106
 ["kk"] = 107
 ["ll"] = 108
 ["mm"] = 109
 ["nn"] = 110
 ["oo"] = 111
 ["pp"] = 112
 ["qq"] = 113
 ["rr"] = 114
 ["ss"] = 115
 ["tt"] = 116
 ["uu"] = 117
 ["vv"] = 118
 ["ww"] = 119
 ["xx"] = 120
 ["yy"] = 121
 ["zz"] = 122
 ["{"] = 123
 ["|"] = 124
 ["}"] = 125
 ["~"] = 126
End Enum

Private Declare Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (ByRef Destination As Any, ByRef Source As Any, ByVal Length As Long)
Private Declare Function GetWindowTextLength Lib "user32.dll" Alias "GetWindowTextLengthA" (ByVal hwnd As Long) As Long
Private Declare Function SendMessage Lib "user32.dll" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByRef lParam As Any) As Long
Private Const EM_SETSEL As Long = &HB1

Private m_txt As TextBox

Private m_objIn As clsBuffer
Private m_objOut As clsBuffer

Private Type typeStyle
 nFontSize As Long
 nMargin As Long
End Type

Private m_tStyle As typeStyle

Private m_objCommands As New Collection
Private m_objHTMLMathSymbol As New Collection
Private m_objMathOperator As New Collection
Private m_objMathOperator2 As New Collection
Private m_sDocumentClass As String
Private m_bIsAppendix As Boolean
Private m_nSection As Long
Private m_nSubSection As Long
Private m_nSubSubSection As Long

Private m_objCite As New Collection, m_nCiteLength As Long
Private m_nCiteCount As Long

Private m_objRef As New Collection

'env name->variant (string array) 0=display name, 1=number within (e.g. section), 2=counter name ("*" means no counter at all)
Private m_objTheorem As New Collection
Private m_objThmCounter(3) As New Collection

Private m_fn As String, m_fnTemp As String, m_fn2 As String

Private m_nTeXIndex As Long
Private m_sTeXHeader As String, m_sTeXHeader2 As String
Private m_sTeXBody As String

Private Sub AddLog(ByVal s As String)
Dim m As Long
If Not m_txt Is Nothing Then
 m = GetWindowTextLength(m_txt.hwnd)
 SendMessage m_txt.hwnd, EM_SETSEL, m, ByVal m
 m_txt.SelText = s + vbCrLf
 DoEvents
End If
End Sub

Private Sub AddWarning(ByVal s As String, Optional ByVal objOut As clsBuffer)
If Not m_objIn Is Nothing Then
 AddLog m_objIn.LineNumber + " Warning: " + s
 If Not objOut Is Nothing Then
  ApplyEnvironment s, "_err"
  objOut.PutString s
 End If
End If
End Sub

Friend Sub SetTextBox(ByVal t As TextBox)
Set m_txt = t
End Sub

Friend Property Get SrcFile() As String
SrcFile = m_fn
End Property

Friend Property Let SrcFile(ByVal s As String)
m_fn = s
m_fnTemp = s + ".temp\"
End Property

Friend Property Get DestFile() As String
DestFile = m_fn2
End Property

Friend Property Let DestFile(ByVal s As String)
m_fn2 = s
End Property

Private Sub ReplaceFromCollection(ByRef s As String, ByVal sName As String, ByVal o As Collection, ByVal sDisplayName As String, ByVal sDefault As String)
Dim l As Long
Dim lps As Long, lpe As Long
Dim s2 As String, s3 As String
Dim e As Long
'///
sName = ChrW(27) + sName
l = Len(sName)
'///
Do
 lps = InStr(1, s, sName)
 If lps = 0 Then Exit Do
 lps = lps + l
 lpe = InStr(lps, s, ChrW(26))
 If lpe = 0 Then Exit Do
 s2 = Mid(s, lps, lpe - lps)
 On Error Resume Next
 Err.Clear
 s3 = m_objCite.Item(StringToHex(s2))
 e = Err.Number
 On Error GoTo 0
 If e Then
  AddLog "Warning: " + sDisplayName + " '" + s2 + "' undefined"
  s3 = sDefault
 End If
 s = Replace(s, sName + s2 + ChrW(26), s3)
Loop
'///
End Sub

Friend Sub Run()
Dim b() As Byte, m As Long
Dim s As String
'///
AddLog String(40, "=")
AddLog "Converting " + m_fn + " to " + m_fn2
'///
On Error Resume Next
Err.Clear
Open m_fn For Binary Access Read As #1
If Err.Number Then
 AddLog "Error: failed to load " + m_fn
 Close
 Exit Sub
Else
 m = LOF(1)
 If m > 0 Then
  ReDim b(m - 1)
  Get #1, 1, b
  s = StrConv(b, vbUnicode)
  s = Replace(s, vbCr, vbNullString)
  s = Replace(s, vbTab, " ")
 End If
End If
Close
On Error GoTo 0
'///
Erase b
Set m_objIn = New clsBuffer
m_objIn.SetString s
s = vbNullString
'///
Set m_objOut = New clsBuffer
'///init default value
m_sDocumentClass = "article"
m_tStyle.nFontSize = 18
m_tStyle.nMargin = 20
m_nCiteLength = 1
m_objCommands.Add "References", StringToHex("refname")
m_objCommands.Add "Abstract", StringToHex("abstractname")
m_objCommands.Add "Fig", StringToHex("figurename")
m_objCommands.Add "Proof", StringToHex("proofname")
'///init simple HTML math symbols
LoadHTMLMathSymbol m_objHTMLMathSymbol
'///
ParseNode m_objOut, "{"
'///
m_objOut.GetString s
Set m_objIn = Nothing
Set m_objOut = Nothing
'///process citation and references
ReplaceFromCollection s, "cite", m_objCite, "cite", "<b>" + String(m_nCiteLength, "?") + "</b>"
ReplaceFromCollection s, "ref-", m_objRef, "reference", "<b>??</b>"
'///output file
On Error Resume Next
Open m_fn2 For Output As #1
If Err.Number Then
 AddLog "Error: failed to save " + m_fn2
 Close
 Exit Sub
Else
 Print #1, "<html><body>"
 If Len(s) > 0 Then Print #1, s
 Print #1, "</body></html>"
End If
Close
On Error GoTo 0
'///generate formula using latex
TeXToPNG
'///over
AddLog "OK!"
End Sub

Private Sub SkipSpaces()
Dim c As Long
Do
 c = m_objIn.GetChar
 If c < 0 Then Exit Sub
 If c = [" "] Then
 Else
  m_objIn.Ungetc
  Exit Do
 End If
Loop
End Sub

Private Sub SkipComment()
Dim c As Long
Do
 c = m_objIn.GetChar
 If c < 0 Then Exit Sub
 If c = ["\n"] Then Exit Do
Loop
SkipSpaces
End Sub

Private Sub SkipSpacesAndComment(Optional ByVal bSkipEmptyLines As Boolean)
Dim c As Long
Do
 c = m_objIn.GetChar
 If c < 0 Then Exit Sub
 If c = [" "] Then
 ElseIf c = ["\n"] Then
  If Not bSkipEmptyLines Then
   m_objIn.Ungetc
   Exit Do
  End If
 ElseIf c = ["%"] Then
  SkipComment
 Else
  m_objIn.Ungetc
  Exit Do
 End If
Loop
End Sub

Private Function ParseCommand() As String
Dim c As Long
Dim s As String
c = m_objIn.GetChar
Select Case c
Case ["a"] To ["z"], ["aa"] To ["zz"]
 s = ChrW(c)
 Do
  c = m_objIn.GetChar
  Select Case c
  Case ["a"] To ["z"], ["aa"] To ["zz"]
   s = s + ChrW(c)
  Case ["*"]
   s = s + ChrW(c)
   Exit Do
  Case Else
   m_objIn.Ungetc
   Exit Do
  End Select
 Loop
Case 32 To 126
 s = ChrW(c)
Case Else
 m_objIn.Ungetc
 Exit Function
End Select
ParseCommand = s
End Function

'FIXME: '{' and ']' can be paired
Private Function ParseOptionalArgument(Optional ByVal bRaw As Boolean) As String
Dim c As Long
Dim nBracketCount As Long
Dim s As String
SkipSpacesAndComment
c = m_objIn.GetChar
If c < 0 Then
 AddWarning "Unexpected end of file"
 Exit Function
ElseIf c = ["lll"] Then
 Do
  c = m_objIn.GetChar
  Select Case c
  Case Is < 0
   AddWarning "Unexpected end of file"
   Exit Function
  Case ["%"]
   If bRaw Then
    s = s + ChrW(c)
   Else
    SkipComment
   End If
  Case ["\"]
   s = s + ChrW(c)
   If Not bRaw Then s = s + ParseCommand
  Case ["\n"]
   s = s + " "
  Case ["{"], ["lll"]
   nBracketCount = nBracketCount + 1
   s = s + ChrW(c)
  Case ["}"], ["rrr"]
   If nBracketCount > 0 Then
    nBracketCount = nBracketCount - 1
    s = s + ChrW(c)
   Else
    If c <> ["rrr"] Then AddWarning "']' expected"
    Exit Do
   End If
  Case Else
   s = s + ChrW(c)
  End Select
 Loop
Else
 m_objIn.Ungetc
End If
ParseOptionalArgument = s
End Function

Private Function ParseArgument(Optional ByVal bRaw As Boolean) As String
Dim c As Long
Dim nBracketCount As Long
Dim s As String
SkipSpacesAndComment
c = m_objIn.GetChar
If c < 0 Then
 AddWarning "Unexpected end of file"
 Exit Function
ElseIf c = ["{"] Then
 Do
  c = m_objIn.GetChar
  Select Case c
  Case Is < 0
   AddWarning "Unexpected end of file"
   Exit Function
  Case ["%"]
   If bRaw Then
    s = s + ChrW(c)
   Else
    SkipComment
   End If
  Case ["\"]
   s = s + ChrW(c)
   If Not bRaw Then s = s + ParseCommand
  Case ["\n"]
   s = s + " "
  Case ["{"]
   nBracketCount = nBracketCount + 1
   s = s + ChrW(c)
  Case ["}"]
   If nBracketCount > 0 Then
    nBracketCount = nBracketCount - 1
    s = s + ChrW(c)
   Else
    Exit Do
   End If
  Case Else
   s = s + ChrW(c)
  End Select
 Loop
ElseIf c = ["\"] Then
 s = s + ChrW(c)
 If Not bRaw Then s = s + ParseCommand
Else
 s = ChrW(c)
End If
ParseArgument = s
End Function

'FIXME: return value is environment-specific (in most cases 0 and useless) and ad-hoc
'sEnvironment: "math"=$ or \( or \) "displaymath"=$$ or \[ or \]
'nParser: 0=use simple HTML, 1=use online parser, 2=use latex executable
Private Function ParseMath(ByVal buf As clsBuffer, ByVal sEnvironment As String, Optional ByVal nParser As Long) As Long
Dim c As Long, i As Long
Dim bEmitChar As Boolean
Dim tmp As New clsBuffer
Dim s As String, s2 As String
Dim sStack() As String, nStackPtr As Long, nStackMax As Long 'environment stack
Dim sStack2() As String, sModifier As String 'currently supported: mathrm, mathbf, mathbb, mathbbm, mathbbold, mathit
Dim sHTMLMath As String 'used when nParser=0
'///
Do
 '///get next char. FIXME: comments unsupported here
 bEmitChar = False
 c = m_objIn.GetChar
 Select Case c
 Case Is < 0
  AddWarning "Unexpected end of file"
  Exit Do
 Case ["0"] To ["9"]
  bEmitChar = True
  If nParser = 0 Then
   '///get modifier
   If sModifier <> vbNullString Then
    s = sModifier
    sModifier = vbNullString
   Else
    s = vbNullString
    For i = nStackPtr - 1 To 0 Step -1
     s = sStack2(i)
     If s <> vbNullString Then Exit For
    Next i
   End If
   '///
   Select Case s
   Case "mathbbold"
    i = &H1D7D8 + (c - ["0"])
    sHTMLMath = sHTMLMath + "&#x" + Hex(i) + ";"
   Case Else
    sHTMLMath = sHTMLMath + ChrW(c)
   End Select
  End If
 Case ["a"] To ["z"], ["aa"] To ["zz"]
  bEmitChar = True
  If nParser = 0 Then
   '///get modifier
   If sModifier <> vbNullString Then
    s = sModifier
    sModifier = vbNullString
   Else
    s = vbNullString
    For i = nStackPtr - 1 To 0 Step -1
     s = sStack2(i)
     If s <> vbNullString Then Exit For
    Next i
   End If
   '///
   Select Case s
   Case "mathrm", "operatorname", "operatorname*"
    sHTMLMath = sHTMLMath + ChrW(c)
   Case "mathbf"
    sHTMLMath = sHTMLMath + "<b>" + ChrW(c) + "</b>"
   Case "mathbb"
    i = c
    If i >= ["aa"] Then i = i - 32
    sHTMLMath = sHTMLMath + "&" + ChrW(i) + "opf;"
   Case "mathbbm"
    i = c
    If i < ["aa"] Then i = i + 32
    sHTMLMath = sHTMLMath + "&" + ChrW(i) + "opf;"
   Case "mathfrak"
    sHTMLMath = sHTMLMath + "&" + ChrW(c) + "fr;"
   Case "mathcal", "mathscr"
    sHTMLMath = sHTMLMath + "&" + ChrW(c) + "scr;"
   Case Else
    sHTMLMath = sHTMLMath + "<i>" + ChrW(c) + "</i>"
   End Select
  End If
 Case ["{"]
  bEmitChar = True
  '///add name to stack
  If nStackPtr >= nStackMax Then
   nStackMax = nStackMax + 256&
   ReDim Preserve sStack(nStackMax - 1), sStack2(nStackMax - 1)
  End If
  sStack(nStackPtr) = "{"
  sStack2(nStackPtr) = sModifier
  sModifier = vbNullString
  nStackPtr = nStackPtr + 1
 Case ["}"]
  If nStackPtr > 0 Then
   If sStack(nStackPtr - 1) = "{" Then
    nStackPtr = nStackPtr - 1
    bEmitChar = True
   End If
  End If
  If Not bEmitChar Then
   AddWarning "'}' without '{'"
  End If
 Case ["$"]
  c = m_objIn.GetChar
  If c = ["$"] Then
   s = "$$"
  Else
   m_objIn.Ungetc
   s = "$"
  End If
  c = 0
  If nStackPtr > 0 Then
   If sStack(nStackPtr - 1) = s Then
    tmp.PutString s
    nStackPtr = nStackPtr - 1
   Else
    c = 1
   End If
  Else
   If (s = "$" And sEnvironment = "math") Or (s = "$$" And sEnvironment = "displaymath") Then
    Exit Do
   Else
    c = 1
   End If
  End If
  If c Then
   nParser = 2 'nested $$, must use latex to parse
   tmp.PutString s
   '///add name to stack
   If nStackPtr >= nStackMax Then
    nStackMax = nStackMax + 256&
    ReDim Preserve sStack(nStackMax - 1), sStack2(nStackMax - 1)
   End If
   sStack(nStackPtr) = s
   nStackPtr = nStackPtr + 1
  End If
 Case ["\"]
  s = ParseCommand
  Select Case s
  Case "(", "["
   nParser = 2 'nested $$, must use latex to parse
   tmp.PutChar ["\"]
   tmp.PutString s
   '///add name to stack
   If nStackPtr >= nStackMax Then
    nStackMax = nStackMax + 256&
    ReDim Preserve sStack(nStackMax - 1), sStack2(nStackMax - 1)
   End If
   sStack(nStackPtr) = s
   nStackPtr = nStackPtr + 1
  Case ")", "]"
   If nStackPtr = 0 Then
    '///we are ready to exit the loop
    If (s = ")" And sEnvironment = "math") Or (s = "]" And sEnvironment = "displaymath") Then
    Else
     AddWarning "'\" + s + "' and '\begin{" + sEnvironment + "}' mismatch"
    End If
    Exit Do
   Else
    nStackPtr = nStackPtr - 1
    Select Case sStack(nStackPtr)
    Case "$"
     tmp.PutChar ["$"]
    Case "$$"
     tmp.PutString "$$"
    Case "("
     tmp.PutString "\)"
    Case "["
     tmp.PutString "\]"
    Case Else
     tmp.PutString "\end{"
     tmp.PutString sStack(nStackPtr)
     tmp.PutChar ["}"]
    End Select
    'TODO: report error if mismatch
'    s = Trim(ParseArgument)
'    If s <> sStack(nStackPtr) Then
'     AddWarning "'\end{" + s + "}' and '\begin{" + sStack(nStackPtr) + "}' mismatch"
'    End If
   End If
  Case "begin"
   If nParser < 1 Then nParser = 1 'simple HTML parser doesn't support this
   tmp.PutString "\begin{"
   '///get name
   s = Trim(ParseArgument)
   tmp.PutString s
   tmp.PutChar ["}"]
   '///check if we need to use latex executable
   If nParser <= 1 Then
    Select Case s
    Case "CD", "tikzpicture", "gathered", "aligned", "alignedat", "array"  '?
     nParser = 2
    End Select
   End If
   '///add name to stack
   If nStackPtr >= nStackMax Then
    nStackMax = nStackMax + 256&
    ReDim Preserve sStack(nStackMax - 1), sStack2(nStackMax - 1)
   End If
   sStack(nStackPtr) = s
   nStackPtr = nStackPtr + 1
  Case "end"
   If nStackPtr = 0 Then
    '///we are ready to exit the loop
    s = Trim(ParseArgument)
    If s <> sEnvironment Then
     AddWarning "'\end{" + s + "}' and '\begin{" + sEnvironment + "}' mismatch"
    End If
    Exit Do
   Else
    nStackPtr = nStackPtr - 1
    Select Case sStack(nStackPtr)
    Case "$"
     tmp.PutChar ["$"]
    Case "$$"
     tmp.PutString "$$"
    Case "("
     tmp.PutString "\)"
    Case "["
     tmp.PutString "\]"
    Case Else
     tmp.PutString "\end{"
     tmp.PutString sStack(nStackPtr)
     tmp.PutChar ["}"]
    End Select
    s = Trim(ParseArgument)
    If s <> sStack(nStackPtr) Then
     AddWarning "'\end{" + s + "}' and '\begin{" + sStack(nStackPtr) + "}' mismatch"
    End If
   End If
  Case Else
   '///check if it is user-defined command
   s2 = CheckUserDefinedCommand(s, True)
   If s2 = vbNullString Then
    tmp.PutChar ["\"]
    tmp.PutString s
    '///check simple HTML mode
    If nParser = 0 Then
     Select Case s
     Case "mathrm", "operatorname", "operatorname*", "mathit", "mathbf", "mathbb", "mathbbm", "mathbbold", "mathfrak", "mathcal", "mathscr"
      sModifier = s
     Case Else
      '///check the symbol list
      On Error Resume Next
      Err.Clear
      s2 = m_objHTMLMathSymbol.Item(StringToHex(s))
      If Err.Number = 0 Then
       'found it
       sHTMLMath = sHTMLMath + s2
      Else
       nParser = 1 'unsupported
      End If
      On Error GoTo 0
     End Select
    End If
    '///check if we must use latex executable
    If nParser <= 1 Then
     Select Case s
     Case "xy", "xymatrix", "displaybreak", "boxed"
      nParser = 2
     Case Else
      If Left(s, 4) = "text" Then
       nParser = 2 'nested text, must use latex to parse
      End If
     End Select
    End If
   Else
    'it is user-defined command
    m_objIn.InsertString s2
   End If
  End Select
 Case ["\n"]
  bEmitChar = True
 Case [" "], ["!"], ["'"], ["("], [")"], ["*"], ["+"], [","], ["-"], ["."], ["/"], [":"], [";"], ["<"], ["="], [">"], ["?"], ["lll"], ["rrr"], ["|"], ["~"]
  bEmitChar = True
  If nParser = 0 Then
   Select Case c
   Case [" "] 'do nothing
   Case ["'"]: sHTMLMath = sHTMLMath + "&prime;"
   Case ["-"]: sHTMLMath = sHTMLMath + "&minus;"
   Case ["<"]: sHTMLMath = sHTMLMath + "&lt;"
   Case [">"]: sHTMLMath = sHTMLMath + "&gt;"
   Case ["~"]: sHTMLMath = sHTMLMath + "&nbsp;"
   Case Else: sHTMLMath = sHTMLMath + ChrW(c)
   End Select
  End If
 Case Else
  If nParser < 1 Then nParser = 1 'simple HTML parser doesn't support this
  bEmitChar = True
 End Select
 '///
 If bEmitChar Then
  tmp.PutChar c
 End If
Loop
'///
Select Case nParser
Case 0 'simple HTML math
 Select Case sEnvironment
 Case "displaymath"
  s = Replace("<p align='center' style='margin: " + CStr(m_tStyle.nMargin) + "px 0px'>\argZ</p>", "\argZ", sHTMLMath)
 Case "math"
  s = sHTMLMath
 Case Else
  s = vbNullString
  AddWarning "Unknown math environment '" + sEnvironment + "'"
 End Select
Case 1 'use online renderer (http://latex.codecogs.com/eqneditor/editor.php)
 '///replace \begin{split} to \begin{align*} (ad-hoc replacement method)
 tmp.GetString s
 s = Replace(s, "\begin{split}", "\begin{align*}")
 s = Replace(s, "\end{split}", "\end{align*}")
 i = Len(s)
 tmp.SetString s
 '///escape char
 s = vbNullString
 For i = 0 To i - 1
  c = tmp.Data(i)
  If c = ["\n"] Then c = [" "]
  Select Case c
  Case ["a"] To ["z"], ["aa"] To ["zz"], ["0"] To ["9"], ["-"], ["*"], ["/"], ["\"], ["^"], ["_"], ["("], [")"], ["lll"], ["rrr"], ["{"], ["}"], ["|"]
   s = s + ChrW(c)
  Case Else
   s = s + "%" + Right("0" + Hex(c), 2)
  End Select
 Next i
 Select Case sEnvironment
 Case "displaymath", "equation*"
  s = Replace("<p align='center' style='margin: " + CStr(m_tStyle.nMargin) + "px 0px'><img src='http://latex.codecogs.com/png.latex?\argZ'></p>", "\argZ", s)
 Case "align*"
  s = Replace("<p align='center' style='margin: " + CStr(m_tStyle.nMargin) + "px 0px'><img src='http://latex.codecogs.com/png.latex?\begin{align*}\argZ\end{align*}'></p>", "\argZ", s)
 Case "equation" 'TODO: equation number
  s = Replace("<p align='center' style='margin: " + CStr(m_tStyle.nMargin) + "px 0px'><img src='http://latex.codecogs.com/png.latex?\argZ'></p>", "\argZ", s)
 Case "math"
  s = Replace("<img src='http://latex.codecogs.com/png.latex?\inline%20\argZ'>", "\argZ", s)
 Case Else
  s = vbNullString
  AddWarning "Unknown math environment '" + sEnvironment + "'"
 End Select
Case 2 'latex executable
 '///
 tmp.GetString s
 m_sTeXBody = m_sTeXBody + "\begin{" + sEnvironment + "}" + s + "\end{" + sEnvironment + "}\pagebreak" + vbLf + vbLf
 '///
 m_nTeXIndex = m_nTeXIndex + 1
 '///
 s2 = m_fnTemp + "tmp" + Format(m_nTeXIndex, "0000")
 s = Replace(s2, "\", "/")
 s = Mid(s, InStrRev(s, "/", Len(m_fnTemp) - 1) + 1) + ".png"
 '///
 Select Case sEnvironment
 Case "equation" 'TODO: equation number
  s = Replace("<p align='center' style='margin: " + CStr(m_tStyle.nMargin) + "px 0px'><img src='\argZ'></p>", "\argZ", s)
 Case "math"
  s = Replace("<img src='\argZ'>", "\argZ", s)
 Case Else
  s = Replace("<p align='center' style='margin: " + CStr(m_tStyle.nMargin) + "px 0px'><img src='\argZ'></p>", "\argZ", s)
 End Select
End Select
'///
buf.PutString s
End Function

Private Function TeXToPNG() As Boolean
Dim s As String
Dim sConvertExecutable As String
Dim e As Long, i As Long
Dim ret As Boolean
'///check if we need to generate png file
If m_nTeXIndex <= 0 Then
 TeXToPNG = True
 Exit Function
End If
'///
AddLog "Generating " + CStr(m_nTeXIndex) + " formula(s) using LaTeX..."
'///
On Error Resume Next
'find convert executable
Err.Clear
sConvertExecutable = g_Config.Item("ConvertExecutable")
If Err.Number <> 0 Or sConvertExecutable = vbNullString Then
 sConvertExecutable = App.Path + "\convert.exe"
End If
Err.Clear
e = 0
If Dir(sConvertExecutable) = vbNullString Then e = -1
e = e Or Err.Number
If e Then
 AddLog "ERROR: Can't find ImageMagick convert.exe executable!"
 Exit Function
End If
'create temp dir
MkDir m_fnTemp
'create temp file
Err.Clear
s = "\batchmode" + vbLf + "\documentclass[a4paper]{article}" + vbLf + "\usepackage{amsmath,amssymb,amsfonts}" + vbLf + _
"\usepackage[top=0in,bottom=0in,left=0in,right=0in]{geometry}" + vbLf + "\usepackage[OT2,T1]{fontenc}" + vbLf + m_sTeXHeader + vbLf + _
"\DeclareSymbolFont{cyrletters}{OT2}{wncyr}{m}{n}" + vbLf + "\DeclareMathSymbol{\Sha}{\mathalpha}{cyrletters}{""58}" + vbLf + m_sTeXHeader2 + vbLf + _
"\begin{document}\pagestyle{empty}" + vbLf + m_sTeXBody + vbLf + "\end{document}"
s = Replace(s, vbCr, vbNullString)
s = Replace(s, vbLf, vbCrLf)
Open m_fnTemp + "tmp.tex" For Output As #1
Print #1, s
Close
If Err.Number Then
 AddLog "ERROR: Failed to save temp file " + m_fnTemp + "tmp.tex"
 Exit Function
End If
On Error GoTo 0
'///
With New clsRunProcess
 If .Run("""pdflatex.exe"" """ + m_fnTemp + "tmp.tex""", m_fnTemp) Then
  .Wait -1
  e = .GetExitCode
  .Detach
  If e Then
   AddLog "ERROR: PDFLaTeX returns error code " + CStr(e)
  End If
 Else
  e = -1
  AddLog "ERROR: Failed to run PDFLaTeX"
 End If
End With
If e Then Exit Function
'///we'd like to create png page by page, reducing disk usage of temp file
ret = True
For i = 1 To m_nTeXIndex
 AddLog "Converting page " + CStr(i) + " of " + CStr(m_nTeXIndex) + "..."
 With New clsRunProcess
  If .Run("""" + sConvertExecutable + """ -verbose -density 150 -trim """ + m_fnTemp + "tmp.pdf[" + CStr(i - 1) + "]"" """ + m_fnTemp + "tmp" + Format(i, "0000") + ".png""", m_fnTemp) Then
   .Wait -1
   e = .GetExitCode
   .Detach
   If e Then
    ret = False
    AddLog "ERROR: convert returns error code " + CStr(e)
   End If
   e = 0
  Else
   e = -1
   ret = False
   AddLog "ERROR: Failed to run convert"
  End If
 End With
 If e Then Exit Function
Next i
'///over
TeXToPNG = ret
End Function

Private Function CheckUserDefinedCommand(ByVal s As String, ByVal IsMathModeAndWebRenderer As Boolean)
Dim s2 As String
s = StringToHex(s)
On Error Resume Next
Err.Clear
s2 = m_objCommands.Item(s)
If Err.Number = 0 Then
 CheckUserDefinedCommand = s2
 Exit Function
End If
If IsMathModeAndWebRenderer Then
 Err.Clear
 s2 = m_objMathOperator.Item(s)
 If Err.Number = 0 Then
  CheckUserDefinedCommand = "\operatorname{" + s2 + "}"
  Exit Function
 End If
 Err.Clear
 s2 = m_objMathOperator2.Item(s)
 If Err.Number = 0 Then
  CheckUserDefinedCommand = "\operatorname*{" + s2 + "}"
  Exit Function
 End If
End If
End Function

'FIXME: return value is environment-specific (in most cases 0 and useless) and ad-hoc
Private Function ParseNode(ByVal buf As clsBuffer, Optional ByVal sEnvironment As String, Optional ByVal sParagraphEnvironment As String, Optional ByVal sLastParagraphEnvironment As String) As Long
Dim c As Long, c2 As Long
Dim c0 As Long 'last char
Dim c00 As Long 'last char put to the buffer
Dim bSpace As Boolean, bEmitChar As Boolean
Dim bParagraph As Boolean, bExit As Boolean
Dim tmp As New clsBuffer, sBuf As String
Dim tmpStyle As typeStyle
Dim s As String, s2 As String, s3 As String
Dim v As Variant
Dim e As Long
Dim objLocalCommand As Collection
'///
Select Case sEnvironment
Case "proof"
 s = Trim(ParseOptionalArgument)
 If s <> vbNullString Then
  Set objLocalCommand = New Collection
  objLocalCommand.Add s, StringToHex("proofname")
 End If
Case "thebibliography"
 m_nCiteLength = Len(Trim(ParseArgument)) 'eat the {99}
Case Else
 '///check if it is theorem environment
 On Error Resume Next
 Err.Clear
 m_objTheorem.Item StringToHex(sEnvironment)
 e = Err.Number
 On Error GoTo 0
 If e = 0 Then
  s = Trim(ParseOptionalArgument)
  If s <> vbNullString Then
   Set objLocalCommand = New Collection
   objLocalCommand.Add s, StringToHex("_thmname")
  End If
 End If
End Select
'///
Do
 '///get next char
 Do
  c = m_objIn.GetChar
  If c = ["%"] Then 'comment
   SkipComment
  ElseIf c = ["\n"] Then
   SkipSpaces
   Exit Do
  Else
   Exit Do
  End If
 Loop
 '///
 bEmitChar = False
 Select Case c
 Case Is < 0
  bExit = True
 Case ["\n"]
  If c0 = ["\n"] Then bParagraph = True '\n\n means end a paragraph
  bSpace = True '\n also add a space
 Case ["{"]
  'begin nested (??)
  ParseNode tmp, "{", , "{"
 Case ["}"]
  'end a nested "{" (??)
  Select Case sEnvironment
  Case "{", "_footnote", "_emph", "_textit", "_textbf", "_texttt", "_textsf", "_textsc", "_textsl", "_section", "_subsection", "_subsubsection", "_section*", "_subsection*", "_subsubsection*"
   bExit = True
  Case Else
   AddWarning "'}' without '{'"
   s = "}"
   ApplyEnvironment s, "_err"
   tmp.PutString s
  End Select
 Case [" "]
  bSpace = True
 Case ["$"]
  'math mode
  c = m_objIn.GetChar
  If c = ["$"] Then
   FlushParagraph sBuf, tmp, sParagraphEnvironment
   bParagraph = True
   ParseMath tmp, "displaymath"
   FlushParagraph sBuf, tmp, "{"
  Else
   m_objIn.Ungetc
   '///ad-hoc space logic
   Select Case c00 'the second character
   Case &H28&, &H5B&, &H7B&, &HB7&, &H2018&, &H201C&, &H3008&, &H300A&, _
   &H300C&, &H300E&, &H3010&, &H3014&, &H3016&, &HFF08&, &HFF0E&, &HFF3B&, _
   &HFF5B&, &HFFE1&, &HFFE5& '([{ etc.
    bSpace = False '??? no space after these characters
   Case &H2E80& To &H9FFF&, &HF900& To &HFAFF& 'CJK
    tmp.PutChar [" "]
   End Select
   '///
   ParseMath tmp, "math"
   '///
   c00 = ["0"] 'ad-hoc??
  End If
 Case ["`"]
  c = m_objIn.GetChar
  If c = ["`"] Then
   c = &H201C&
  Else
   m_objIn.Ungetc
   c = &H2018&
  End If
  bEmitChar = True
 Case ["'"]
  c = m_objIn.GetChar
  If c = ["'"] Then
   c = &H201D&
  Else
   m_objIn.Ungetc
   c = &H2019&
  End If
  bEmitChar = True
 Case ["""]
  c = &H201D&
  bEmitChar = True
 Case ["-"]
  c = m_objIn.GetChar
  If c = ["-"] Then
   c = m_objIn.GetChar
   If c = ["-"] Then
    c = &H2014&
   Else
    m_objIn.Ungetc
    c = &H2013&
   End If
  Else
   m_objIn.Ungetc
   c = ["-"]
  End If
  bEmitChar = True
 Case ["~"]
  c00 = 0
  c0 = 0
  tmp.PutString "&nbsp;"
 Case ["?"]
  If m_objIn.GetChar = ["`"] Then
   tmp.PutString "&#xBF;"
  Else
   m_objIn.Ungetc
   bEmitChar = True
  End If
 Case ["!"]
  If m_objIn.GetChar = ["`"] Then
   tmp.PutString "&#xA1;"
  Else
   m_objIn.Ungetc
   bEmitChar = True
  End If
 Case ["\"]
  s = ParseCommand
  s2 = CheckUserDefinedCommand(s, False) 'check if it is user-defined command
  If s = vbNullString Then
   AddWarning "Expect a command name after '\'"
  ElseIf s2 <> vbNullString Then
   m_objIn.InsertString s2
  Else
   Select Case s
   Case "#", "$", "%", "&", "_", "{", "}"
    c = AscW(s)
    bEmitChar = True
   Case "^", "~", "`", "'", """", "=", ".", "u", "v", "H", "c", "d", "b"
    c = AscW(s)
    s2 = ParseArgument
    Select Case Len(s2)
    Case 0
     If c = ["^"] Or c = ["~"] Then
      bEmitChar = True
     Else
      c = 0
     End If
    Case 1
     Select Case c
     Case ["`"]: s3 = "00C0            00C8            00CC                01F800D2                    00D9    1E80    1EF2    00E0            00E8            00EC                01F900F2                    00F9    1E81    1EF3"
     Case ["'"]: s3 = "00C1    0106    00C9    01F4    00CD    1E3001391E3E014300D31E54    0154015A    00DA    1E82    00DD017900E1    0107    00E9    01F5    00ED    1E31013A1E3F014400F31E55    0155015B    00FA    1E83    00FD017A"
     Case ["^"]: s3 = "00C2    0108    00CA    011C012400CE0134                00D4            015C    00DB    0174    01761E9000E2    0109    00EA    011D012500EE0135                00F4            015D    00FB    0175    01771E91"
     Case ["""]: s3 = "00C4            00CB        1E2600CF                    00D6                    00DC    1E841E8C0178    00E4            00EB        1E2700EF                    00F6                1E9700FC    1E851E8D00FF"
     Case ["~"]: s3 = "00C3            1EBC            0128                00D100D5                    01681E7C        1EF8    00E3            1EBD            0129                00F100F5                    01691E7D        1EF9"
     Case ["="]: s3 = "0100        00D00112    1E200126012A                    014C                    016A            0232    0101        01110113    1E210127012B                    014D                    016B            0233"
     Case ["."]: s3 = "02261E02010A1E0A01161E1E01201E220130        013F1E401E44022E1E56    1E581E601E6A        1E861E8A1E8E017B02271E03010B1E0B01171E1F01211E23            01401E411E45022F1E57    1E591E611E6B        1E871E8B1E8F017C"
     Case ["u"]: s3 = "0102            0114    011E    012C                    014E                    016C                    0103            0115    011F    012D                    014F                    016D"
     Case ["v"]: s3 = "01CD    010C010E011A    01E6021E01CF    01E8        014701D1        01580160016401D3                017D01CE    010D    011B    01E7021F01D001F001E9        014801D2        01590161    01D4                017E"
     Case ["h"]: s3 = "                                                        0150                    0170                                                                            0151                    0171"
     Case ["c"]: s3 = "        00C7    0228    01221E28        0136013B    0145            0156015E0162                                00E7    0229        1E29        0137013C    0146            0157015F0163"
     Case ["d"]: s3 = "1EA01E04    1E0C1EB8        1E241ECA    1E321E361E421E461ECC        1E5A1E621E6C1EE41E7E1E88    1EF41E921EA11E05    1E0D1EB9        1E251ECB    1E331E371E431E471ECD        1E5B1E631E6D1EE51E7F1E89    1EF51E93"
     Case ["b"]: s3 = "    1E06    1E0E        01E4            1E341E3A    1E48            1E5E    1E6E                    1E94    1E07    1E0F        01E51E96        1E351E3B    1E49            1E5F    1E6F                    1E95"
     End Select
     '///
     c2 = AscW(s2) And &HFFFF&
     Select Case c2
     Case ["a"] To ["z"]
      s3 = Trim(Mid(s3, c2 * 4& - 259&, 4))
     Case ["aa"] To ["zz"]
      s3 = Trim(Mid(s3, c2 * 4& - 283&, 4))
     Case Else
      s3 = vbNullString
     End Select
     If Len(s3) > 0 Then
      tmp.PutString "&#x" + s3 + ";"
     Else
      tmp.PutString "&#" + CStr(c2) + ";"
      Select Case c
      Case ["`"]: c2 = &H300
      Case ["'"]: c2 = &H301
      Case ["^"]: c2 = &H302
      Case ["""]: c2 = &H308
      Case ["~"]: c2 = &H303
      Case ["="]: c2 = &H304
      Case ["."]: c2 = &H307
      Case ["u"]: c2 = &H306
      Case ["v"]: c2 = &H30C
      Case ["h"]: c2 = &H30B
      Case ["c"]: c2 = &H327
      Case ["d"]: c2 = &H323
      Case ["b"]: c2 = &H331
      End Select
      tmp.PutString "&#" + CStr(c2) + ";"
     End If
    Case Else
     c = 0
    End Select
    If c = 0 Then
     s = "\" + s + "{" + s2 + "}"
     AddWarning "'" + s + "' is currently unsupported"
     ApplyEnvironment s, "_err"
     tmp.PutString s
    End If
   Case "-" 'do nothing
   Case "\"
    tmp.PutString "<br>"
    bSpace = False
    c00 = 0
    c0 = 0
    c = 0
   Case "(" 'math mode
    ParseMath tmp, "math"
   Case "[" 'math mode
    FlushParagraph sBuf, tmp, sParagraphEnvironment
    bParagraph = True
    ParseMath tmp, "displaymath"
    FlushParagraph sBuf, tmp, "{"
   Case "backslash"
    c = ["\"]
    bEmitChar = True
   Case "dots"
    c = &H2026&
    bEmitChar = True
   Case "quad"
    tmp.PutString "&#x3000;"
   Case "qquad"
    tmp.PutString "&#x3000;&#x3000;"
   Case " "
    tmp.PutString "&#x2000;"
   Case "AA"
    tmp.PutString "&#xC5;"
   Case "aa"
    tmp.PutString "&#xE5;"
   Case "i"
    tmp.PutString "&#x131;"
   Case "L"
    tmp.PutString "&#x141;"
   Case "l"
    tmp.PutString "&#x142;"
   Case "O"
    tmp.PutString "&#xD8;"
   Case "o"
    tmp.PutString "&#xF8;"
   Case "S"
    tmp.PutString "&sect;"
   Case "SS"
    tmp.PutString "&#x1E9E;"
   Case "ss"
    tmp.PutString "&#xDF;"
   Case "AE"
    tmp.PutString "&#xC6;"
   Case "OE"
    tmp.PutString "&#x152;"
   Case "ae"
    tmp.PutString "&#xE6;"
   Case "oe"
    tmp.PutString "&#x153;"
   Case "euro"
    tmp.PutString "&#x20AC;"
   Case "begin"
    c = 0 '&H1=style changed
    s = Trim(ParseArgument)
    '///check if we need to flush paragraph
    Select Case s
    Case "displaymath", "enumerate", "itemize", "flushleft", "center", "flushright", "quote", "abstract", "thebibliography", "proof", _
    "equation", "equation*", _
    "eqnarray", "eqnarray*", "multline", "multline*", "gather", "gather*", "align", "align*", "flalign", "flalign*", "alignat", "alignat*"
     FlushParagraph sBuf, tmp, sParagraphEnvironment
     bParagraph = True
    Case Else
     '///check if it is theorem environment
     On Error Resume Next
     Err.Clear
     m_objTheorem.Item StringToHex(s)
     e = Err.Number
     On Error GoTo 0
     If e = 0 Then
      FlushParagraph sBuf, tmp, sParagraphEnvironment
      bParagraph = True
     End If
    End Select
    If Not bParagraph Then
     'flush space at least
     If bSpace Then
      tmp.PutChar [" "]
      bSpace = False
     End If
    End If
    '///check if we need to change style
    Select Case s
    Case "quote", "abstract"
     c = c Or 1
     tmpStyle = m_tStyle
     m_tStyle.nFontSize = 16
     m_tStyle.nMargin = 16
    Case "thebibliography"
     c = c Or 1
     tmpStyle = m_tStyle
     m_tStyle.nFontSize = 14
     m_tStyle.nMargin = 4
    End Select
    '///parse environment
    Select Case s
    Case "math", "displaymath", "equation", "equation*", "align*"
     ParseMath tmp, s
    Case "eqnarray", "eqnarray*", "multline", "multline*", "gather", "gather*", "align", "flalign", "flalign*", "alignat", "alignat*"
     ParseMath tmp, s, 2
    Case "flushleft", "center", "flushright"
     ParseNode tmp, s, "_" + s, "_" + s
    Case "thebibliography"
     m_nCiteCount = 0
     ParseNode tmp, s
    Case Else
     ParseNode tmp, s
    End Select
    '///check if we need to flush paragraph
    If bParagraph Then
     FlushParagraph sBuf, tmp, "{"
    End If
    '///check if we need to restore style
    If c And 1 Then
     m_tStyle = tmpStyle
    End If
    '///check if we should exit
    Select Case s
    Case "document"
     bExit = True
    End Select
   Case "end"
    Select Case sEnvironment
    Case "_li", "_bibitem"
     m_objIn.Ungetc 4 'FIXME: super ad-hoc code!!!
    Case Else
     s = Trim(ParseArgument)
     If s <> sEnvironment Then
      s = "\end{" + s + "}"
      AddWarning "'" + s + "' and '\begin{" + sEnvironment + "}' mismatch"
      ApplyEnvironment s, "_err"
      tmp.PutString s
     End If
    End Select
    bExit = True
   Case "item"
    Select Case sEnvironment
    Case "_li"
     m_objIn.Ungetc 5 'FIXME: super ad-hoc code!!!
     bExit = True
    Case "enumerate", "itemize"
     FlushParagraph sBuf, tmp, sParagraphEnvironment
     ParseNode tmp, "_li"
     FlushParagraph sBuf, tmp, "{"
    Case Else
     s = "\" + s
     AddWarning "'" + s + "' is not allowed in environment '" + sEnvironment + "'"
     ApplyEnvironment s, "_err"
     tmp.PutString s
    End Select
   Case "bibitem"
    Select Case sEnvironment
    Case "_bibitem"
     m_objIn.Ungetc 8 'FIXME: super ad-hoc code!!!
     bExit = True
    Case "thebibliography"
     s = Trim(ParseArgument)
     s2 = StringToHex(s)
     m_nCiteCount = m_nCiteCount + 1
     On Error Resume Next
     Err.Clear
     m_objCite.Add CStr(m_nCiteCount), s2
     If Err.Number Then AddWarning "'\bibitem{" + s + "}' is already defined"
     On Error GoTo 0
     FlushParagraph sBuf, tmp, sParagraphEnvironment
     ParseNode tmp, "_bibitem"
     FlushParagraph sBuf, tmp, "{"
    Case Else
     s = "\" + s
     AddWarning "'" + s + "' is not allowed in environment '" + sEnvironment + "'"
     ApplyEnvironment s, "_err"
     tmp.PutString s
    End Select
   Case "footnote", "emph", "textit", "textbf", "texttt", "textsf", "textsc", "textsl", "section", "subsection", "subsubsection", "section*", "subsection*", "subsubsection*"
    '///check if we need to flush space
    Select Case s
    Case "emph", "textit", "textbf", "texttt", "textsf", "textsc", "textsl"
     If bSpace Then
      tmp.PutChar [" "]
      bSpace = False
     End If
    End Select
    '///check if we need to flush paragraph
    Select Case s
    Case "section", "subsection", "subsubsection", "section*", "subsection*", "subsubsection*"
     FlushParagraph sBuf, tmp, sParagraphEnvironment
     bParagraph = True
    End Select
    '///
    SkipSpacesAndComment
    c = m_objIn.GetChar
    If c = ["{"] Then
     ParseNode tmp, "_" + s, "{", "{"
    Else
     AddWarning "Expect '{' after '\" + s + "'"
    End If
    '///check if we need to flush paragraph
    If bParagraph Then
     Select Case m_sDocumentClass
     Case "amsart"
      Select Case s
      Case "subsection", "subsubsection", "subsection*", "subsubsection*"
       bParagraph = False
       SkipSpacesAndComment True
      End Select
     End Select
    End If
    If bParagraph Then
     FlushParagraph sBuf, tmp, "{"
    End If
   Case "newcommand", "renewcommand"
    ParseNewCommand s, m_objCommands, AscW(s) = ["rr"]
   Case "newtheorem"
    s = Trim(ParseArgument) 'name
    s2 = Trim(ParseOptionalArgument) 'use existing counter?
    s3 = Trim(ParseArgument) 'displayname
    If s2 = vbNullString Then
     s2 = Trim(ParseOptionalArgument) 'use section number?
     v = Array(s3, s2, vbNullString)
    Else
     On Error Resume Next
     Err.Clear
     m_objTheorem.Item StringToHex(s2)
     If Err.Number Then AddWarning "Theorem environment '" + s2 + "' undefined"
     On Error GoTo 0
     v = Array(s3, vbNullString, s2)
    End If
    On Error Resume Next
    Err.Clear
    m_objTheorem.Add v, StringToHex(s)
    If Err.Number Then AddWarning "Theorem environment '" + s + "' is aready defined"
    On Error GoTo 0
   Case "newtheorem*"
    s = Trim(ParseArgument) 'name
    s2 = Trim(ParseArgument) 'displayname
    v = Array(s2, vbNullString, "*")
    On Error Resume Next
    Err.Clear
    m_objTheorem.Add v, StringToHex(s)
    If Err.Number Then AddWarning "Theorem environment '" + s + "' is aready defined"
    On Error GoTo 0
   Case "DeclareMathOperator"
    ParseNewCommand s, m_objMathOperator, False
   Case "DeclareMathOperator*"
    ParseNewCommand s, m_objMathOperator2, False
   Case "appendix"
    If Not m_bIsAppendix Then
     m_bIsAppendix = True
     m_nSection = 0
     m_nSubSection = 0
     m_nSubSubSection = 0
    End If
   Case "verb"
    c2 = m_objIn.GetChar
    s = vbNullString
    Do
     c = m_objIn.GetChar
     If c < 0 Then
      AddWarning "Unexpected end of file"
      Exit Do
     End If
     If c = c2 Then Exit Do
     If c = ["\n"] Then c = [" "]
     s = s + ChrW(c)
    Loop
    s = Replace(s, "&", "&amp;")
    s = Replace(s, "<", "&lt;")
    s = Replace(s, ">", "&gt;")
    s = Replace(s, " ", "&nbsp;")
    ApplyEnvironment s, "_verb"
    tmp.PutString s
   Case "cite"
    s2 = Trim(ParseOptionalArgument)
    s = Trim(ParseArgument)
    If s2 <> vbNullString Then
     tmp.PutString "[" + ChrW(27) + "cite" + s + ChrW(26)
     m_objIn.InsertString ", " + s2 + "]"
    Else
     tmp.PutString "[" + ChrW(27) + "cite" + s + ChrW(26) + "]"
    End If
   Case "ref"
    s = Trim(ParseArgument)
    tmp.PutString ChrW(27) + "ref-" + s + ChrW(26)
   Case "eqref"
    s = Trim(ParseArgument)
    tmp.PutString "(" + ChrW(27) + "ref-" + s + ChrW(26) + ")"
   Case "documentclass"
    ParseOptionalArgument
    m_sDocumentClass = Trim(ParseArgument)
   Case "usepackage", "RequirePackage"
    'cnrrently only used in latex output
    s2 = Trim(ParseOptionalArgument)
    s3 = Trim(ParseArgument)
    If s3 <> vbNullString Then ParsePackages s3
    If s3 <> vbNullString Then
     If s2 <> vbNullString Then s2 = "[" + s2 + "]"
     m_sTeXHeader = m_sTeXHeader + "\" + s + s2 + "{" + s3 + "}" + vbLf
    End If
   Case "emit" 'fake-command: emit HTML code directly. usage: same as \verb
    c2 = m_objIn.GetChar
    s = vbNullString
    Do
     c = m_objIn.GetChar
     If c < 0 Then
      AddWarning "Unexpected end of file"
      Exit Do
     End If
     If c = c2 Then Exit Do
     s = s + ChrW(c)
    Loop
    tmp.PutString s
   Case Else
    s = "\" + s
    AddWarning "Unknown command '" + s + "'"
    ApplyEnvironment s, "_err"
    tmp.PutString s
   End Select
  End If
 Case Else
  bEmitChar = True
 End Select
 '///
 If bExit Or bParagraph Then
  If tmp.CurrentPosition > 0 Then
   tmp.GetString s
   tmp.Clear
   If bExit Then
    ApplyEnvironment s, sLastParagraphEnvironment
   Else
    ApplyEnvironment s, sParagraphEnvironment
   End If
   sBuf = sBuf + s
  End If
  bParagraph = False
  bSpace = False
  c00 = 0
  c0 = 0
  c = 0
  If bExit Then Exit Do
 Else
  If bEmitChar Then
   '///FIXME: some ad-hoc space logic
   Select Case c 'the first character
   Case &H21&, &H29&, &H2C&, &H2E&, &H3A&, &H3B&, &H3F&, &H5D&, _
   &H7D&, &HA8&, &HB7&, &H2C7&, &H2C9&, &H2015&, &H2016&, &H2019&, _
   &H201D&, &H2026&, &H2236&, &H3001&, &H3002&, &H3003&, &H3005&, &H3009&, _
   &H300B&, &H300D&, &H300F&, &H3011&, &H3015&, &H3017&, &HFF01&, &HFF02&, _
   &HFF07&, &HFF09&, &HFF0C&, &HFF0E&, &HFF1A&, &HFF1B&, &HFF1F&, &HFF3D&, _
   &HFF40&, &HFF5C&, &HFF5D&, &HFF5E&, &HFFE0& '!),.:;?]} etc.
    bSpace = False '??? no space before these characters
   Case &H2E80& To &H9FFF&, &HF900& To &HFAFF& 'CJK
    Select Case c00 'the second character
    Case ["0"] To ["9"], ["a"] To ["z"], ["aa"] To ["zz"]
     bSpace = True
    Case &H2E80& To &H9FFF&, &HF900& To &HFAFF& 'CJK
     bSpace = False
    End Select
   End Select
   Select Case c00 'the second character
   Case &H28&, &H5B&, &H7B&, &HB7&, &H2018&, &H201C&, &H3008&, &H300A&, _
   &H300C&, &H300E&, &H3010&, &H3014&, &H3016&, &HFF08&, &HFF0E&, &HFF3B&, _
   &HFF5B&, &HFFE1&, &HFFE5& '([{ etc.
    bSpace = False '??? no space after these characters
   Case &H2E80& To &H9FFF&, &HF900& To &HFAFF& 'CJK
    Select Case c 'the first character
    Case ["0"] To ["9"], ["a"] To ["z"], ["aa"] To ["zz"]
     bSpace = True
    End Select
   End Select
   '///
   If bSpace And c00 <> 0 And c0 <> 0 Then
    tmp.PutString "&nbsp;" '??
    'tmp.PutString " " '??
   End If
   Select Case c
   Case ["<"]
    tmp.PutString "&lt;"
   Case [">"]
    tmp.PutString "&gt;"
   Case ["&"]
    tmp.PutString "&amp;"
   Case Else
    tmp.PutChar c
   End Select
   bSpace = False
   If c Then c00 = c
  End If
  If c Then c0 = c
 End If
Loop
'///
If Len(sBuf) > 0 Then
 Select Case sEnvironment
 Case "flushleft", "center", "flushright", "document"
  sEnvironment = "{"
 End Select
 ApplyEnvironment sBuf, sEnvironment, objLocalCommand
 buf.PutString sBuf
End If
End Function

Private Sub ParsePackages(ByRef s As String)
Dim v() As String, s1 As String
Dim i As Long
v = Split(s, ",")
s = vbNullString
For i = LBound(v) To UBound(v)
 s1 = Trim(v(i))
 Select Case s1
 Case "amsmath", "amssymb", "amsfonts", "geometry", "fontenc" 'already included
 Case Else
  If s <> vbNullString Then s = s + ","
  s = s + s1
 End Select
Next i
End Sub

Private Sub ParseNewCommand(ByVal s As String, ByVal obj As Collection, ByVal IsRenew As Boolean)
Dim s2 As String, s3 As String
s2 = Trim(ParseArgument)
If Left(s2, 1) <> "\" Then
 AddWarning "Expect a command after '\" + s + "'"
Else
 s2 = Trim(Mid(s2, 2))
 s3 = StringToHex(s2)
 On Error Resume Next
 Err.Clear
 obj.Remove s3
 If Err.Number Then
  'command doesn't exist
  If IsRenew Then
   AddWarning "Command '\" + s2 + "' doesn't exist when using '\" + s + "'"
  End If
 Else
  'command exists
  If Not IsRenew Then
   AddWarning "Command '\" + s2 + "' already exists when using '\" + s + "'"
  End If
 End If
 On Error GoTo 0
 obj.Add Trim(ParseArgument), s3
End If
End Sub

Private Sub FlushParagraph(ByRef sBuf As String, ByVal tmp As clsBuffer, ByVal sEnvironment As String)
Dim s As String
If tmp.CurrentPosition > 0 Then
 tmp.GetString s
 tmp.Clear
 ApplyEnvironment s, sEnvironment
 sBuf = sBuf + s
End If
End Sub

Private Sub BeginSection()
m_nSubSection = 0
'///reset theorem counter
Set m_objThmCounter(1) = New Collection
'///
BeginSubSection
End Sub

Private Sub BeginSubSection()
m_nSubSubSection = 0
'///reset theorem counter
Set m_objThmCounter(2) = New Collection
'///
BeginSubSubSection
End Sub

Private Sub BeginSubSubSection()
'///reset theorem counter
Set m_objThmCounter(3) = New Collection
'///
End Sub

Private Sub ApplyEnvironment(ByRef s As String, ByVal sEnvironment As String, Optional ByVal objLocalCommand As Collection)
Dim s2 As String, s3 As String
Dim v As Variant
Dim e As Long
Dim tmp As New clsBuffer
'///
If objLocalCommand Is Nothing Then Set objLocalCommand = m_objCommands
'///
Select Case sEnvironment
Case vbNullString, "_flushleft"
 s = Replace("<p style='margin: " + CStr(m_tStyle.nMargin) + "px 0px; font-size: " + CStr(m_tStyle.nFontSize) + "px; font-family: serif'>\argZ</p>", "\argZ", s)
Case "_center"
 s = Replace("<p align='center' style='margin: " + CStr(m_tStyle.nMargin) + "px 0px; font-size: " + CStr(m_tStyle.nFontSize) + "px; font-family: serif'>\argZ</p>", "\argZ", s)
Case "_flushright"
 s = Replace("<p align='right' style='margin: " + CStr(m_tStyle.nMargin) + "px 0px; font-size: " + CStr(m_tStyle.nFontSize) + "px; font-family: serif'>\argZ</p>", "\argZ", s)
Case "_section"
 m_nSection = m_nSection + 1
 BeginSection
 If m_bIsAppendix Then
  s2 = ChrW(64 + m_nSection)
  s3 = "#60a000"
 Else
  s2 = CStr(m_nSection)
  s3 = "#0070c0"
 End If
 Select Case m_sDocumentClass
 Case "amsart"
  s2 = "<h2 style='margin: 8px 0px 0px; padding: 0px; font-size: 18px; font-family: serif; font-weight: normal; font-variant: small-caps; color: #3e3e3e; height: 32px; line-height: 18px; border-bottom-color: #e3e3e3; border-bottom-width: 1px; border-bottom-style: solid; text-align: justify; word-wrap: break-word !important; background-color: #ffffff;'>" + _
  "<p align='center'><span style='padding: 0px 2px 7px; color: \argY; line-height: 28px; border-bottom-color: \argY; border-bottom-width: 2px; border-bottom-style: solid; word-wrap: break-word !important;'>" + _
  "<span style='padding: 4px 10px; border-top-left-radius: 80%; border-top-right-radius: 100%; border-bottom-right-radius: 90%; border-bottom-left-radius: 20%; color: white; word-wrap: break-word !important; background-color: \argY; margin-right: 8px;'>" + s2 + "</span>" + _
  "<strong style='line-height: 24px; word-wrap: break-word !important;'>\argZ</strong></span></p></h2>"
 Case Else
  s2 = "<h2 style='margin: 8px 0px 0px; padding: 0px; font-size: 18px; font-family: serif; font-weight: normal; color: #3e3e3e; height: 32px; line-height: 18px; border-bottom-color: #e3e3e3; border-bottom-width: 1px; border-bottom-style: solid; text-align: justify; word-wrap: break-word !important; background-color: #ffffff;'>" + _
  "<span style='padding: 0px 2px 3px; color: \argY; line-height: 28px; border-bottom-color: \argY; border-bottom-width: 2px; border-bottom-style: solid; float: left; display: block; word-wrap: break-word !important;'>" + _
  "<span style='padding: 4px 10px; border-top-left-radius: 80%; border-top-right-radius: 100%; border-bottom-right-radius: 90%; border-bottom-left-radius: 20%; color: white; word-wrap: break-word !important; background-color: \argY; margin-right: 8px;'>" + s2 + "</span>" + _
  "<strong style='line-height: 24px; word-wrap: break-word !important;'>\argZ</strong></span></h2>"
 End Select
 s2 = Replace(s2, "\argY", s3)
 s = Replace(s2, "\argZ", s)
Case "_section*"
 BeginSection
 If m_bIsAppendix Then
  s3 = "#60a000"
 Else
  s3 = "#0070c0"
 End If
 Select Case m_sDocumentClass
 Case "amsart"
  s2 = "<h2 style='margin: 8px 0px 0px; padding: 0px; font-size: 18px; font-family: serif; font-weight: normal; font-variant: small-caps; color: #3e3e3e; height: 32px; line-height: 18px; border-bottom-color: #e3e3e3; border-bottom-width: 1px; border-bottom-style: solid; text-align: justify; word-wrap: break-word !important; background-color: #ffffff;'>" + _
  "<p align='center'><span style='padding: 0px 2px 7px; color: \argY; line-height: 28px; border-bottom-color: \argY; border-bottom-width: 2px; border-bottom-style: solid; word-wrap: break-word !important;'>" + _
  "<strong style='line-height: 24px; word-wrap: break-word !important;'>\argZ</strong></span></p></h2>"
 Case Else
  s2 = "<h2 style='margin: 8px 0px 0px; padding: 0px; font-size: 18px; font-family: serif; font-weight: normal; color: #3e3e3e; height: 32px; line-height: 18px; border-bottom-color: #e3e3e3; border-bottom-width: 1px; border-bottom-style: solid; text-align: justify; word-wrap: break-word !important; background-color: #ffffff;'>" + _
  "<span style='padding: 0px 2px 3px; color: \argY; line-height: 28px; border-bottom-color: \argY; border-bottom-width: 2px; border-bottom-style: solid; float: left; display: block; word-wrap: break-word !important;'>" + _
  "<strong style='line-height: 24px; word-wrap: break-word !important;'>\argZ</strong></span></h2>"
 End Select
 s2 = Replace(s2, "\argY", s3)
 s = Replace(s2, "\argZ", s)
Case "_subsection"
 m_nSubSection = m_nSubSection + 1
 BeginSubSection
 If m_bIsAppendix Then
  s2 = ChrW(64 + m_nSection)
  s3 = "#406000"
 Else
  s2 = CStr(m_nSection)
  s3 = "#005080"
 End If
 s2 = s2 + "." + CStr(m_nSubSection)
 Select Case m_sDocumentClass
 Case "amsart"
  s2 = "<span style='color: white; border-top-left-radius: 5px; border-top-right-radius: 5px; border-bottom-right-radius: 5px; border-bottom-left-radius: 5px; padding: 2px 6px; background-color: \argY;'>" + s2 + _
  "</span><span style='color: \argY; padding: 4px 10px;'>\argZ.</span>&nbsp;"
 Case Else
  s2 = "<p style='margin: 20px 0px; font-size: 18px; font-family: serif'>" + _
  "<span style='color: white; border-top-left-radius: 5px; border-top-right-radius: 5px; border-bottom-right-radius: 5px; border-bottom-left-radius: 5px; padding: 4px 6px; background-color: \argY;'>" + s2 + _
  "</span><span style='color: \argY; padding: 4px 10px;'>\argZ</span></p>"
 End Select
 s2 = Replace(s2, "\argY", s3)
 s = Replace(s2, "\argZ", s)
Case "_subsection*"
 BeginSubSection
 If m_bIsAppendix Then
  s3 = "#406000"
 Else
  s3 = "#005080"
 End If
 Select Case m_sDocumentClass
 Case "amsart"
  s2 = "<span style='color: white; border-top-left-radius: 5px; border-top-right-radius: 5px; border-bottom-right-radius: 5px; border-bottom-left-radius: 5px; padding: 2px 6px; background-color: \argY;'>\argZ.</span>&nbsp;"
 Case Else
  s2 = "<p style='margin: 20px 0px; font-size: 18px; font-family: serif'>" + _
  "<span style='color: white; border-top-left-radius: 5px; border-top-right-radius: 5px; border-bottom-right-radius: 5px; border-bottom-left-radius: 5px; padding: 4px 6px; background-color: \argY;'>\argZ</span></p>"
 End Select
 s2 = Replace(s2, "\argY", s3)
 s = Replace(s2, "\argZ", s)
Case "_subsubsection"
 m_nSubSubSection = m_nSubSubSection + 1
 BeginSubSubSection
 If m_bIsAppendix Then
  s2 = ChrW(64 + m_nSection)
  s3 = "#304000"
 Else
  s2 = CStr(m_nSection)
  s3 = "#003050"
 End If
 s2 = s2 + "." + CStr(m_nSubSection) + "." + CStr(m_nSubSubSection)
 Select Case m_sDocumentClass
 Case "amsart"
  s2 = "<span style='color: white; border-top-left-radius: 5px; border-top-right-radius: 5px; border-bottom-right-radius: 5px; border-bottom-left-radius: 5px; padding: 0px 6px; background-color: \argY;'>" + s2 + _
  "</span><span style='color: \argY; padding: 1px 10px;'>\argZ.</span>&nbsp;"
 Case Else
  s2 = "<p style='margin: 20px 0px; font-size: 18px; font-family: serif'>" + _
  "<span style='color: white; border-top-left-radius: 5px; border-top-right-radius: 5px; border-bottom-right-radius: 5px; border-bottom-left-radius: 5px; padding: 1px 6px; background-color: \argY;'>" + s2 + _
  "</span><span style='color: \argY; padding: 1px 10px;'>\argZ</span></p>"
 End Select
 s2 = Replace(s2, "\argY", s3)
 s = Replace(s2, "\argZ", s)
Case "_subsubsection*"
 BeginSubSubSection
 If m_bIsAppendix Then
  s3 = "#304000"
 Else
  s3 = "#003050"
 End If
 Select Case m_sDocumentClass
 Case "amsart"
  s2 = "<span style='color: white; border-top-left-radius: 5px; border-top-right-radius: 5px; border-bottom-right-radius: 5px; border-bottom-left-radius: 5px; padding: 0px 6px; background-color: \argY;'>\argZ.</span>&nbsp;"
 Case Else
  s2 = "<p style='margin: 20px 0px; font-size: 18px; font-family: serif'>" + _
  "<span style='color: white; border-top-left-radius: 5px; border-top-right-radius: 5px; border-bottom-right-radius: 5px; border-bottom-left-radius: 5px; padding: 1px 6px; background-color: \argY;'>\argZ</span></p>"
 End Select
 s2 = Replace(s2, "\argY", s3)
 s = Replace(s2, "\argZ", s)
Case "{" '??? do nothing
Case "enumerate"
 s = Replace("<ol>\argZ</ol>", "\argZ", s)
Case "itemize"
 s = Replace("<ul>\argZ</ul>", "\argZ", s)
Case "thebibliography"
 On Error Resume Next
 Err.Clear
 s2 = Trim(objLocalCommand.Item(StringToHex("refname")))
 e = Err.Number
 On Error GoTo 0
 If e = 0 And s2 <> vbNullString Then
  s2 = s2 + "}"
  m_objIn.InsertString s2
  ParseNode tmp, "_section*", "{", "{"
  tmp.GetString s2
 Else
  s2 = vbNullString
 End If
 s = s2 + Replace("<ol>\argZ</ol>", "\argZ", s)
Case "quote"
 's = Replace("<blockquote style='border: 2px dashed #b6b6b6; background-color: #f4f4f4; margin: 0px; color: #333333; padding: 0px " + CStr(m_tStyle.nMargin) + "px'>\argZ</blockquote>", "\argZ", s)
 s = Replace("<blockquote style='border: 2px dashed #b6b6b6; margin: 10px 0px; padding: 5px 10px 5px 45px; color: #333333; vertical-align: baseline; background-image: url(http://wx.suzazi.com/Public/wxeditor/css/left_quote.jpg); background-color: #f1f1f1; background-position: 1% 5px; background-repeat: no-repeat no-repeat;'>\argZ</blockquote>", "\argZ", s)
Case "abstract"
 On Error Resume Next
 Err.Clear
 s2 = Trim(objLocalCommand.Item(StringToHex("abstractname")))
 e = Err.Number
 On Error GoTo 0
 If e = 0 And s2 <> vbNullString Then
  s2 = s2 + "}"
  m_objIn.InsertString s2
  ParseNode tmp, "{", "{", "{"
  tmp.GetString s2
  s = "<p style='margin: " + CStr(m_tStyle.nMargin) + "px 0px; font-size: " + CStr(m_tStyle.nFontSize) + "px; font-family: serif'><span style='color: white; border-top-left-radius: 5px; border-top-right-radius: 5px; border-bottom-right-radius: 5px; border-bottom-left-radius: 5px; padding: 4px 4px; background-color: red;'>" + s2 + "</span></p>" + s
 End If
 s = Replace("<blockquote style='padding: 1px " + CStr(m_tStyle.nMargin) + "px; margin: 0px 5px; color: #333333; box-sizing: border-box; orphans: 2; widows: 2; text-align: justify; border-top-left-radius: 5px; border-top-right-radius: 5px; border-bottom-right-radius: 5px; border-bottom-left-radius: 5px; box-shadow: #eeeeee 3px 3px 3px 3px; border-left-width: 3px; border-left-style: solid; border-left-color: red; background-color: white;'>\argZ</blockquote>", "\argZ", s)
Case "proof"
 On Error Resume Next
 Err.Clear
 s2 = Trim(objLocalCommand.Item(StringToHex("proofname")))
 e = Err.Number
 On Error GoTo 0
 If e = 0 And s2 <> vbNullString Then
  s2 = s2 + ".}"
  m_objIn.InsertString s2
  ParseNode tmp, "{", "{", "{"
  tmp.GetString s2
  s = "<p style='margin: " + CStr(m_tStyle.nMargin) + "px 0px; font-size: " + CStr(m_tStyle.nFontSize) + "px; font-family: serif'><span style='color: white; border-top-left-radius: 5px; border-top-right-radius: 5px; border-bottom-right-radius: 5px; border-bottom-left-radius: 5px; padding: 3px 3px; background-color: rgb(0, 80, 176);'>" + s2 + "</span></p>" + s
 End If
 s = Replace("<blockquote style='margin: 0px; border-left-width: 5px; border-left-style: solid; border-left-color: rgb(0, 80, 176); padding: 0px 10px'>\argZ</blockquote>", "\argZ", s)
Case "_li", "_bibitem"
 s = Replace("<li>\argZ</li>", "\argZ", s)
Case "_footnote"
 'font size is fixed (?)
 s = Replace("<span style='font-size: 14px; color: gray'>(\argZ)</span>", "\argZ", s)
Case "_emph", "_textit", "_textsl"
 s = Replace("<em>\argZ</em>", "\argZ", s)
Case "_textbf"
 s = Replace("<span style='font-weight: bold; background-color: yellow;'>\argZ</span>", "\argZ", s)
Case "_textsf"
 s = Replace("<span style='font-family: sans-serif'>\argZ</span>", "\argZ", s)
Case "_textsc"
 s = Replace("<span style='font-variant: small-caps'>\argZ</span>", "\argZ", s)
Case "_verb", "_texttt"
 s = Replace("<span style='font-family: monospace'>\argZ</span>", "\argZ", s)
Case "_err"
 s = Replace("<span style='font-family: monospace; color: red; border: 1px solid gray; background-color: lightgray'>\argZ</span>", "\argZ", s)
Case Else
 On Error Resume Next
 '///check if it is theorem environment
 Err.Clear
 v = m_objTheorem.Item(StringToHex(sEnvironment))
 If Err.Number = 0 Then
  '///get optional theorem name
  Err.Clear
  s2 = Trim(objLocalCommand.Item(StringToHex("_thmname")))
  If Err.Number Then s2 = vbNullString
  If s2 = vbNullString Then
   s2 = ".}"
  Else
   s2 = " (" + s2 + ").}"
  End If
  '///get theorem name and number
  If v(2) = vbNullString Then
   v(2) = sEnvironment
  Else
   Err.Clear
   s3 = m_objTheorem.Item(StringToHex(v(2)))(1)
   If Err.Number = 0 Then v(1) = s3
  End If
  If v(2) = "*" Then 'no counter at all
   s2 = v(0) + s2
  Else 'has counter
   If m_bIsAppendix Then s3 = ChrW(64 + m_nSection) Else s3 = CStr(m_nSection)
   '///
   Select Case v(1)
   Case "section"
    e = IncreaseAndGetCounter(m_objThmCounter(1), v(2))
    s2 = v(0) + " " + s3 + "." + CStr(e) + s2
   Case "subsection"
    e = IncreaseAndGetCounter(m_objThmCounter(2), v(2))
    s2 = v(0) + " " + s3 + "." + CStr(m_nSubSection) + "." + CStr(e) + s2
   Case "subsubsection"
    e = IncreaseAndGetCounter(m_objThmCounter(3), v(2))
    s2 = v(0) + " " + s3 + "." + CStr(m_nSubSection) + "." + CStr(m_nSubSubSection) + "." + CStr(e) + s2
   Case Else
    e = IncreaseAndGetCounter(m_objThmCounter(0), v(2))
    s2 = v(0) + " " + CStr(e) + s2
   End Select
  End If
  '///
  m_objIn.InsertString s2
  ParseNode tmp, "{", "{", "{"
  tmp.GetString s2
  s = "<p style='margin: " + CStr(m_tStyle.nMargin) + "px 0px; font-size: " + CStr(m_tStyle.nFontSize) + "px; font-family: serif'><span style='color: white; border-top-left-radius: 5px; border-top-right-radius: 5px; border-bottom-right-radius: 5px; border-bottom-left-radius: 5px; padding: 3px 3px; background-color: rgb(0, 176, 80);'>" + s2 + "</span></p>" + s
  '///
  s = Replace("<blockquote style='border: 2px dashed #80d7a7; background-color: #f4f4f4; margin: 0px; color: #333333; padding: 0px " + CStr(m_tStyle.nMargin) + "px'>\argZ</blockquote>", "\argZ", s)
 Else
  s = Replace("<span style='font-family: monospace; color: red; border: 1px solid gray; background-color: lightgray'>\begin{\argZ}</span>", "\argZ", sEnvironment) _
  + s + Replace("<span style='font-family: monospace; color: red; border: 1px solid gray; background-color: lightgray'>\end{\argZ}</span>", "\argZ", sEnvironment)
  AddWarning "Unknown environment '" + sEnvironment + "'"
 End If
End Select
End Sub

Private Function IncreaseAndGetCounter(ByVal o As Collection, ByVal sName As String) As Long
On Error Resume Next
Dim e As Long
sName = StringToHex(sName)
e = 0
e = Val(o.Item(sName))
e = e + 1
o.Remove sName
o.Add CStr(e), sName
IncreaseAndGetCounter = e
End Function
